;(function (name) {
    window[name] = {
        install: (app, options) => {
            let components = window.components
            if (components) {
                for (const key in components) {
                    const component = components[key]
                    app.component(component.name || key, component)
                }
            }

            function onFn(name, data) {
                return new Promise((resolve, reject) => {
                    const id = this.commandId++
                    this.commands.set(id, {
                        resolve,
                        reject
                    })
                    hbuilderx.postMessage({
                        id,
                        command: name,
                        data
                    })
                })
            }

            function onFnScript(name, method, ...args) {
                return this.onFn('onFnScript', {
                    name,
                    method,
                    args
                })
            }

            function onEmit(name, data) {
                const event = new CustomEvent(name, {
                    detail: data
                })
                window.dispatchEvent(event)
            }
            const events = {
                channel: 'window-message-channel',
                id: 1,
                messages: new Map(),
                actions: new Map(),
                started: false,
                emit(name, data) {
                    return new Promise((resolve, reject) => {
                        const id = this.id++
                        this.messages.set(id, {
                            resolve,
                            reject
                        })
                        const event = new CustomEvent(this.channel, {
                            detail: {
                                data,
                                id,
                                command: name
                            }
                        })
                        window.dispatchEvent(event)
                    })
                },
                on(name, cb) {
                    this.actions.set(name, cb)
                    if (this.started) {
                        return
                    }
                    window.addEventListener(this.channel, async event => {
                        const { id, command, data } = event.detail
                        const { resolve, reject } = this.messages.get(id)
                        try {
                            const callback = this.actions.get(command)
                            const response = await callback?.(data)
                            resolve(response)
                        } catch (error) {
                            reject(error)
                        }
                    })
                    this.started = true
                }
            }
            function setTheme(theme) {
                const style = document.documentElement.style
                for (const key in theme) {
                    if (key != 'name') {
                        const value = theme[key]
                        style.setProperty('--' + key, value)
                    }
                }
                this.onThemeChanged?.(theme.name)
            }

            const connection = window.Vue.ref({
                isConnected: true,
                host: ''
            })
            app.provide('connection', connection)

            function initChannel() {
                window.addEventListener('hbuilderxReady', () => {
                    hbuilderx.onDidReceiveMessage(async msg => {
                        if (msg.type == 'DialogButtonEvent') {
                            if (msg.button == '关闭') {
                                this.onFn('onDialogClose')
                            } else {
                                this?.onButtonClick(msg.button)
                            }
                        } else {
                            const { id, _id, command, data, hasError } = msg
                            if (id) {
                                if (this.commands.has(id)) {
                                    const { resolve, reject } = this.commands.get(id)
                                    if (hasError) {
                                        return reject(new Error(data))
                                    }
                                    resolve(data)
                                }
                            } else if (_id) {
                                let response
                                let hasError = false
                                try {
                                    if (command == 'onConnectionClosed') {
                                        connection.value.isConnected = false
                                        connection.value.host = data
                                        console.log('连接状态', connection)
                                    } else if (command == 'onConnectionOpened') {
                                        connection.value.isConnected = true
                                        connection.value.host = data
                                        console.log('连接状态', connection)
                                    }
                                    response = await this[command]?.call(this, data)
                                } catch (error) {
                                    response = error
                                    hasError = true
                                }
                                hbuilderx.postMessage({
                                    data: response,
                                    _id,
                                    hasError
                                })
                            }
                        }
                    })
                    setTimeout(() => {
                        this.onFn('initTheme').then(this.setTheme)
                        this.onInit()
                        onEmit('init')
                        window.isInit = true
                    }, 0)
                })
            }

            function onIconLoad(svgs) {
                const container = document.getElementById('lepton-svgs')
                const ids = new Set()
                for (const node of container?.children || []) {
                    if (node.tagName === 'symbol') {
                        ids.add(node.id)
                    }
                }
                let text = svgs.reduce((value, it) => {
                    if (ids.has(it.id)) {
                        return value
                    }
                    value += it.symbol
                    ids.add(it.id)
                    return value
                }, '')
                if (container) {
                    container.insertAdjacentHTML('beforeend', text)
                } else {
                    const svg = '<svg id="lepton-svgs" style="display: none;">' + text + '</svg>'
                    document.body.insertAdjacentHTML('afterbegin', svg)
                }
            }

            function buildToast() {
                const app = window.Vue.createApp({
                    data() {
                        return {
                            default: {
                                duration: 3000,
                                type: 'info',
                                position: 'top-right',
                                actions: []
                            },
                            areas: [],
                            msgIndex: 0
                        }
                    },
                    mounted() {
                        events.on('show-toast', this.show)
                        events.on('hide-toast', this.hide)
                    },
                    methods: {
                        toastContainerClass(area) {
                            const items = []
                            switch (area.id) {
                                case 'top-left':
                                    items.push('toast-top-left')
                                    break
                                case 'top-right':
                                    items.push('toast-top-right')
                                    break
                                case 'bottom-left':
                                    items.push('toast-bottom-left')
                                    break
                                case 'bottom-right':
                                    items.push('toast-bottom-right')
                                    break
                                case 'top-full':
                                    items.push('toast-top-full-width')
                                    break
                                case 'bottom-full':
                                    items.push('toast-bottom-full-width')
                                    break
                            }
                            return items
                        },
                        toastMessageClass(msg) {
                            const items = ['toast']
                            switch (msg.type) {
                                case 'success':
                                    items.push('toast-success')
                                    break
                                case 'error':
                                    items.push('toast-error')
                                    break
                                case 'warning':
                                    items.push('toast-warning')
                                    break
                                case 'info':
                                    items.push('toast-info')
                                    break
                                case 'loading':
                                    items.push('toast-loading')
                                    break
                            }
                            return items
                        },
                        hide(areaId, msgId) {
                            const area = this.areas.find(it => it.id == areaId)
                            const idx = area.children.findIndex(it => (it.id = msgId))
                            area.children.splice(idx, 1)
                        },
                        hideMS(areaId, msgId, ms = -1) {
                            if (ms > 0) {
                                const id = setTimeout(() => {
                                    this.hide(areaId, msgId)
                                    clearTimeout(id)
                                }, ms)
                            } else {
                                this.hide(areaId, msgId)
                            }
                        },
                        show(data) {
                            let msg = Object.assign({}, this.default, data)
                            msg.id = this.msgIndex + 1
                            msg.duration ||= this.default.duration
                            if (msg.duration != -1) {
                                const id = setTimeout(() => {
                                    this.hide(msg.position, msg.id)
                                    clearTimeout(id)
                                }, msg.duration)
                            }
                            msg.close = duration => this.hideMS(msg.position, msg.id, duration)
                            let area = this.areas.find(it => it.id == msg.position)
                            if (area) {
                                area.children.push(msg)
                                msg = area.children[area.children.length - 1]
                            } else {
                                this.areas.push({
                                    id: msg.position,
                                    children: [msg]
                                })
                                area = this.areas[this.areas.length - 1]
                            }

                            return {
                                area,
                                msg
                            }
                        },
                        onButtonClick(area, item, action) {
                            action.onClick(area, item, action)
                        }
                    },
                    template: `
<div class="toast-container" v-for="area in areas" :class="toastContainerClass(area)" aria-live="polite" role="alert">
    <div :class="toastMessageClass(item)" style="" v-for="item in area.children">
        <div class="toast-message">
            {{item.msg}}
        </div>
        <button v-for="action in item.actions" @click="onButtonClick(area,item,action)" :disabled="action.disabled">{{action.label}}</button>
    </div>
</div>`
                })
                const container = document.createElement('div')
                container.id = 'toast'
                document.body.appendChild(container)
                app.mount(container)
                return {
                    async success(msg, duration, options) {
                        const opts = {
                            msg,
                            duration,
                            type: 'success',
                            ...options
                        }
                        return await events.emit('show-toast', opts)
                    },
                    async info(msg, duration, options) {
                        const opts = {
                            msg,
                            duration,
                            type: 'info',
                            ...options
                        }
                        return await events.emit('show-toast', opts)
                    },
                    async warn(msg, duration, options) {
                        const opts = {
                            msg,
                            duration,
                            type: 'warning',
                            ...options
                        }
                        return await events.emit('show-toast', opts)
                    },
                    async error(msg, duration, options) {
                        const opts = {
                            msg,
                            duration,
                            type: 'error',
                            ...options
                        }
                        return await events.emit('show-toast', opts)
                    }
                }
            }

            const toast = buildToast()

            function onError(msg) {
                toast.error(msg, 5000)
            }

            app.config.globalProperties.initChannel = initChannel
            app.config.globalProperties.commandId = 1
            app.config.globalProperties.commands = new Map()
            app.config.globalProperties.onFn = onFn
            app.config.globalProperties.onFnScript = onFnScript
            app.config.globalProperties.onEmit = onEmit
            app.config.globalProperties.onIconLoad = onIconLoad
            app.config.globalProperties.setTheme = setTheme
            app.config.globalProperties.onError = onError
            app.config.globalProperties.$toast = toast
            app.provide('onFn', onFn)
        }
    }
})('lepton')
